﻿#region using namespaces

using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using RSteel.DAL;
using RSteel.DAO;

#endregion

namespace RSteel.Utils.SiteMap
{
    /// An extremely simple AccessSiteMapProvider that only supports a
    /// site map node hierarchy 1 level deep.
    public class SqlSiteMapProvider : StaticSiteMapProvider
    {
        // This string is case sensitive.
        private readonly object syncRoot = new object();

        // Some basic state to help track the initialization state of the provider.
        private bool initialized;
        private SiteMapNode rootNode;

        public virtual bool IsInitialized
        {
            get { return initialized; }
        }

        // Return the root node of the current site map.
        public override SiteMapNode RootNode
        {
            get
            {
                SiteMapNode tempRootNode = BuildSiteMap();
                return tempRootNode;
            }
        }

        protected override SiteMapNode GetRootNodeCore()
        {
            return RootNode;
        }

        // Initialize is used to initialize the properties and any state that the
        // AccessProvider holds, but is not used to build the site map.
        // The site map is built when the BuildSiteMap method is called.
        public override void Initialize(string name, NameValueCollection attributes)
        {
            if (IsInitialized)
                return;

            base.Initialize(name, attributes);

            // Create and test the connection to the Microsoft Access database.

            // Retrieve the Value of the Access connection string from the
            // attributes NameValueCollection.

            initialized = true;
        }

        ///
        /// SiteMapProvider and StaticSiteMapProvider methods that this derived class must override.
        ///
        // Clean up any collections or other state that an instance of this may hold.
        protected override void Clear()
        {
            lock (syncRoot)
            {
                rootNode = null;
                base.Clear();
            }
        }

        // Build an in-memory representation from persistent
        // storage, and return the root node of the site map.
        public override SiteMapNode BuildSiteMap()
        {
            // Return immediately if this method has been called before
            if (rootNode != null)
            {
                return rootNode;
            }

            // Query the database for site map nodes
            List<DAL.SiteMap> siteMaps;
            using (RSteelDbDataContext context = BaseContext.GetDataContext())
            {
                siteMaps = (from siteMapEl in context.SiteMaps
                            orderby siteMapEl.Id
                            select siteMapEl).ToList();
            }

            DAL.SiteMap rootMapNode = (from siteMap in siteMaps
                                       where siteMap.ParentId == null
                                       select siteMap).Single();

            rootNode = createSiteMapNode(rootMapNode);

            AddNode(rootNode, null);

            parseChildNodes(rootMapNode, siteMaps);

            return rootNode;
        }

        private void parseChildNodes(DAL.SiteMap rootNodeParam, IEnumerable<DAL.SiteMap> siteMaps)
        {
            List<DAL.SiteMap> childSiteMaps = (from siteMapEl in siteMaps
                                               where rootNodeParam.Id == siteMapEl.ParentId
                                               select siteMapEl).ToList();

            SiteMapNode rootMapNode = createSiteMapNode(rootNodeParam);

            foreach (DAL.SiteMap childSiteMap in childSiteMaps)
            {
                SiteMapNode mapNode = createSiteMapNode(childSiteMap);

                AddNode(mapNode, rootMapNode);

                parseChildNodes(childSiteMap, siteMaps);
            }
        }

        private SiteMapNode createSiteMapNode(DAL.SiteMap mapNode)
        {
            var siteMapNode = new SiteMapNode(this,
                                              mapNode.Id.ToString(),
                                              mapNode.Url,
                                              mapNode.Title,
                                              mapNode.Description);
            string roleNames = mapNode.Roles.Trim();
            if (!string.IsNullOrEmpty(roleNames))
            {
                string[] roleList = roleNames.Split(new[] {',', ';'}, 512);
                siteMapNode.Roles = roleList;
            }

            return siteMapNode;
        }
    }
}